001    /*
002     * Copyright 2004-2005 Stephen J. McConnell.
003     * Copyright 2004 Niclas Hedhman
004     * Licensed  under the  Apache License,  Version 2.0  (the "License");
005     * you may not use  this file  except in  compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     *   http://www.apache.org/licenses/LICENSE-2.0
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed  under the  License is distributed on an "AS IS" BASIS,
012     * WITHOUT  WARRANTIES OR CONDITIONS  OF ANY KIND, either  express  or
013     * implied.
014     *
015     * See the License for the specific language governing permissions and
016     * limitations under the License.
017     */
018    
019    package net.dpml.transit.artifact;
020    
021    import java.io.IOException;
022    import java.io.PrintWriter;
023    
024    import java.net.URL;
025    import java.net.URLConnection;
026    import java.net.URLStreamHandler;
027    import java.security.AccessController;
028    import java.security.PrivilegedAction;
029    
030    import net.dpml.transit.Transit;
031    import net.dpml.transit.TransitRuntimeException;
032    import net.dpml.transit.SecuredTransitContext;
033    
034    /**
035     * The artifact URL protocol handler.
036     * @author <a href="http://www.dpml.net">Digital Product Meta Library</a>
037     * @version 1.0.1
038     */
039    public class Handler extends URLStreamHandler
040    {
041        // ------------------------------------------------------------------------
042        // static
043        // ------------------------------------------------------------------------
044    
045       /**
046        * Default buffer size.
047        */
048        private static final int BUFFER_SIZE = 100;
049    
050        // ------------------------------------------------------------------------
051        // state
052        // ------------------------------------------------------------------------
053    
054       /**
055        * The transit context.
056        */
057        private SecuredTransitContext   m_context;
058    
059        // ------------------------------------------------------------------------
060        // constructor
061        // ------------------------------------------------------------------------
062    
063       /**
064        * Creation of a new transit artifact protocol handler.
065        */
066        public Handler()
067        {
068            try
069            {
070                Transit.getInstance();
071                m_context = SecuredTransitContext.getInstance();
072            }
073            catch( RuntimeException e )
074            {
075                e.printStackTrace();
076                throw e;
077            }
078        }
079    
080        // ------------------------------------------------------------------------
081        // implementation
082        // ------------------------------------------------------------------------
083    
084        /**
085         * Opens a connection to the specified URL.
086         *
087         * @param url A URL to open a connection to.
088         * @return The established connection.
089         * @throws IOException If a connection failure occurs.
090         */
091        protected URLConnection openConnection( final URL url )
092          throws IOException
093        {
094            return new ArtifactURLConnection( url, m_context );
095        }
096    
097       /**
098        * Return the external representation of the supplied url.
099        * @param u the url
100        * @return a string representing of the url as an artifact uri
101        */
102        protected String toExternalForm( URL u )
103        {
104            StringBuffer buf = new StringBuffer( BUFFER_SIZE );
105            buf.append( u.getProtocol() );
106            buf.append( ":" );
107            String path = getRealPath( u );
108            if( path != null )
109            {
110                int lastPos = path.length() - 1;
111                if( path.charAt( lastPos ) == '/' )
112                {
113                    buf.append( path.substring( 0, lastPos ) );
114                }
115                else
116                {
117                    buf.append( path );
118                }
119            }
120    
121            String internal = getInternalPath( u );
122            if( null != internal )
123            {
124                buf.append( internal );
125            }
126    
127            String version = u.getUserInfo();
128            if( ( version != null ) && !"".equals( version ) )
129            {
130                buf.append( '#' );
131                buf.append( version );
132            }
133            String result = buf.toString();
134            buf.setLength( 0 );
135            return result;
136        }
137    
138       /**
139        * Return the pure artifact path without any internal address.
140        * @param url the url to evaluate
141        * @return the pure path
142        */
143        private String getRealPath( URL url )
144        {
145            String path = url.getPath();
146            if( null == path )
147            {
148                return null;
149            }
150            int index = path.indexOf( "!" );
151            if( index < 0 )
152            {
153                return path;
154            }
155            else
156            {
157                return path.substring( 0, index );
158            }
159        }
160    
161       /**
162        * Return the value of an internal address associated with a path.
163        * @param url the url to evaluate
164        * @return the internal address of null if the url does not contain an internal address
165        */
166        private String getInternalPath( URL url )
167        {
168            String path = url.getPath();
169            if( null == path )
170            {
171                return null;
172            }
173            int index = path.indexOf( "!" );
174            if( index < 0 )
175            {
176                return null;
177            }
178            else
179            {
180                return path.substring( index );
181            }
182        }
183    
184    
185       /**
186        * Parse the supplied specification.
187        * @param dest the destination url to populate
188        * @param spec the supplied spec
189        * @param start the starting position
190        * @param limit the limit
191        */
192        protected void parseURL( final URL dest, String spec, int start, int limit )
193        {
194            try
195            {
196                final String protocol = dest.getProtocol();
197                String specPath = spec.substring( start, limit );
198                String path = dest.getPath();
199    
200                if( path == null )
201                {
202                    path = specPath;
203                    if( !path.endsWith( "/" ) && ( path.indexOf( "!" ) < 0 ) )
204                    {
205                        path = path + "/";
206                    }
207                }
208                else
209                {
210                    int lastPos = path.length() - 1;
211                    if( path.charAt( lastPos ) == '/' )
212                    {
213                        path = path.substring( 0, lastPos );
214                    }
215                    if( specPath.charAt( 0 ) == '/' )
216                    {
217                        path = path + "!" + specPath;
218                    }
219                    else
220                    {
221                        path = path + "!/" + specPath;
222                    }
223                }
224                String version = dest.getUserInfo();
225                if( version == null )
226                {
227                    if( limit < spec.length() )
228                    {
229                        version = spec.substring( limit + 1 );
230                    }
231                }
232                final String user = version;
233                final String authority = null;
234                final int port = -1;
235                final String host = null;
236                final String query = null;
237                final String ref = null;
238                final String finalPath = path;
239                AccessController.doPrivileged( 
240                  new PrivilegedAction()
241                  {
242                    public Object run()
243                    {
244                        setURL( dest, protocol, host, port, authority, user, finalPath, query, ref );
245                        return null;
246                    }
247                  }
248                );
249            }
250            catch( Throwable e )
251            {
252                try
253                {
254                    PrintWriter log = Transit.getInstance().getLogWriter();
255                    String message = "Unable to parse the URL: "
256                        + dest + ", " + spec + ", " + start + ", " + limit;
257                    log.println( message );
258                    log.println( "---------------------------------------------------" );
259                    e.printStackTrace( log );
260                }
261                catch( TransitRuntimeException f )
262                {
263                    // Panic!!!! Should not happen.
264                    f.printStackTrace();
265                    e.printStackTrace();
266                }
267            }
268        }
269    }